#ifndef PILO_ENVIRONMENT_VARIABLE_INFORMATIONS
#define PILO_ENVIRONMENT_VARIABLE_INFORMATIONS

#include "common/common.h"
#include "string/stringmanipulations.h"
#include "string/stringoutput.h"
#include "memory/pilotblockallocator.h"

using namespace PILO::Core::Memory;

namespace PILO {
    namespace Core {
        namespace Information 
        {

            template<typename T> 
            class PILOTEnvironmentVariable {
            public:
                PILOTEnvironmentVariable(PILOIMemoryAllocator * memoryAllocator) { 
                    _name = MC_NULL_POINTER(T);
                    _value = MC_NULL_POINTER(T); 
                    if (memoryAllocator != MC_NULL_POINTER(PILOIMemoryAllocator)) {
                        _memoryAllocator = memoryAllocator;
                    } else {
                        _memoryAllocator = &_defaultMemoryAllocator;
                    }
                }

                ~PILOTEnvironmentVariable() {
                    freeBoth();
                    _memoryAllocator = MC_NULL_POINTER(PILOIMemoryAllocator);
                }

                T* name() const {return _name; }
                T* value() const  {return _value; }
                const T* constName() const {return _name; }
                const T* constValue() const  {return _value; }

                BOOL setName(const T * srcString, size_t length) {
                    freeName();

                    _name = StringAllocCopy(srcString, length, _memoryAllocator);
                    if (_name == MC_NULL_POINTER(T)) {
                        return FALSE;
                    }
                    return TRUE;
                }

                BOOL setValue(const T * srcString, size_t length) 
                {
                    freeValue();
                    _value = StringAllocCopy(srcString, length, _memoryAllocator);
                    if (_value == MC_NULL_POINTER(T)) {
                        return FALSE;
                    }
                    return TRUE;
                }


                void freeName()
                {
                    if (_name != MC_NULL_POINTER(T)) {
                        MemoryFree(_name);
                        _name = MC_NULL_POINTER(T);
                    }
                }

                void freeValue()
                {
                    if (_value != MC_NULL_POINTER(T)) {
                        MemoryFree(_value);
                        _value = MC_NULL_POINTER(T);
                    }
                }
                BOOL freeBoth()
                {
                    freeName();
                    freeValue();
                }

                const T * toString(T * destBuffer, size_t maxDestBufferSize) const
                {
                    FormatOutputToString(destBuffer,maxDestBufferSize, TEXT("[%s] := (%s)"), _name, _value);
                    return destBuffer;
                }

            private:
                T* _name;
                T* _value;
                PILOHeapAllocator _defaultMemoryAllocator;
                PILOIMemoryAllocator * _memoryAllocator;

            private: //methods
            };

            template<typename T> 
            class PILOTEnvironmentVariableExtractor
            {
            public:
                PILOTEnvironmentVariableExtractor(::PILO::Core::Memory::PILOIMemoryAllocator * memoryAllocator = MC_NULL_POINTER(::PILO::Core::Memory::PILOIMemoryAllocator)) 
                {
                    _internalData = MC_NULL_POINTER(PILOTEnvironmentVariable<T> *);
                    if (memoryAllocator != MC_NULL_POINTER(PILOIMemoryAllocator)) {
                        _memoryAllocator = memoryAllocator;
                    } else {
                        _memoryAllocator = &_defaultMemoryAllocator;
                    }
                    _count = 0;
                    _successfulCount = 0;
                }
                

                BOOL extractAllEnvironmentVariables() {
                    PTSTR pEnvBlock = GetEnvironmentStrings();
                    // Parse the block with the following format:
                    // =::=::\
                    // =...
                    // var=value\0
                    // ...
                    // var=value\0\0
                    // Note that some other strings might begin with '='.
                    // Here is an example when the application is started from a network share.
                    // [0] =::=::\
                    // [1] =C:=C:\Windows\System32
                    // [2] =ExitCode=00000000
                    //

                    PTSTR pszCurrent = pEnvBlock;
                    HRESULT hr = S_OK;
                    PCTSTR pszPos = NULL;
                    int current = 0;

                    while (pszCurrent != NULL) {
                        current ++;
                        while (*pszCurrent != TEXT('\0'))
                            pszCurrent++;
                        pszCurrent++;
                        // Check if it was not the last string.
                        if (*pszCurrent == TEXT('\0'))
                            break;
                    } 

                    size_t countSave = current;

                    _internalData = (PILOTEnvironmentVariable<T> **) _memoryAllocator->alloc(countSave * sizeof(PILOTEnvironmentVariable<T> *));
                    if (_internalData == MC_NULL_POINTER(PILOTEnvironmentVariable<T> *))
                    {
                        return FALSE;
                    }
                    

                    //start extracting
                    //reset variables
                    pszPos = NULL;
                    pszCurrent = pEnvBlock;
                    current = 0;

                    while (pszCurrent != NULL) {
                        // handle the meaningless strings like:
                        // "=::=::\"
                        if (*pszCurrent == TEXT('=')) {
                            // Look for '=' separator.
                            pszPos = _tcschr(pszCurrent+1, TEXT('='));
                        } else {
                            // Look for '=' separator.
                            pszPos = _tcschr(pszCurrent, TEXT('='));

                        }

                       
                          
                        // Point now to the first character of the value.
                        pszPos++;
                        // Copy the variable name.
                        size_t cbNameLength = // Without the' ='
                            (size_t)pszPos - (size_t)pszCurrent - sizeof(TCHAR);
                        
                        _internalData[_successfulCount] = new PILOTEnvironmentVariable<T>(_memoryAllocator);
                        if (_internalData[_successfulCount] == MC_NULL_POINTER(PILOTEnvironmentVariable<T>)) {
                            return FALSE;
                        }
                        _internalData[_successfulCount]->setName(pszCurrent, cbNameLength);
                        _internalData[_successfulCount]->setValue(pszPos, _tcslen(pszPos)+1);
                        _successfulCount ++;

                        // Next variable please.
                        current++;
                        // Move to the end of the string.
                        while (*pszCurrent != TEXT('\0'))
                            pszCurrent++;
                        pszCurrent++;
                        // Check if it was not the last string.
                        if (*pszCurrent == TEXT('\0'))
                            break;
                    };
                    // Don't forget to free the memory.
                    FreeEnvironmentStrings(pEnvBlock);

                    _count = countSave;
                    return TRUE;
                }

                void freeData()
                {
                    for (int i=0; i<_successfulCount; i++) {
                        MP_SAFE_DELETE(_internalData[i]);
                    }

                    _memoryAllocator->free(_internalData);
                }

                void consoleDebug() const 
                {
                    T tmpBuffer[1024];
                    for (int i=0; i<_successfulCount; i++) {
                        _internalData[i]->toString(tmpBuffer, MCF_COUNT_OF(tmpBuffer));
                        FormatOutputToConsole(TEXT("%d %s\n"), i,  tmpBuffer);
                    }
                }
                

            private:
                PILOTEnvironmentVariable<T> ** _internalData;
                size_t  _count;
                size_t  _successfulCount;

                PILOHeapAllocator _defaultMemoryAllocator;
                PILOIMemoryAllocator * _memoryAllocator;

            private: //method

            };

        }
    }
}

#endif /* PILO_ENVIRONMENT_VARIABLE_MANAGER */
